package smallfrench.moteur.interp.err;

import java.awt.Color;
import java.util.ArrayList;

import smallfrench.moteur.interp.ISortieConsole;

/**
 * Un {@link IntercepteurErreur} qui affiche l’erreur interceptée sur une
 * {@link ISortieConsole}.
 * 
 * @author antoine1023
 *
 * @param <T> La classe d’erreur interceptée
 */
public abstract class AfficheurErreur<T> extends IntercepteurErreur {

	private final ArrayList<Ecouteur> ecouteurs;

	private final ISortieConsole sortieErreur;

	/**
	 * 
	 * @param classeInterceptable
	 * @param sortieErreur
	 */
	public AfficheurErreur(Class<T> classeInterceptable,
			ISortieConsole sortieErreur) {

		super(classeInterceptable);

		if (sortieErreur == null)
			throw new NullPointerException();

		this.sortieErreur = sortieErreur;

		ecouteurs = new ArrayList<Ecouteur>();
	}

	public void ajouter(Ecouteur e) {
		ecouteurs.add(e);
	}

	/**
	 * Informe les écouteurs, prépare la sortie, puis appelle
	 * <code>afficher()</code>.
	 */
	@SuppressWarnings("unchecked")
	@Override
	public final void intercepterImpl(Object erreur) {

		for (Ecouteur e : ecouteurs)
			e.onErreur(erreur);

		sortieErreur.setPremierPlan(Color.RED);
		sortieErreur.ecrireLigne();
		afficher(sortieErreur, (T) erreur);
	}

	/**
	 * Affiche l’erreur sur la sortie.<br>
	 * La console est déjà "préparée" : texte en rouge, etc...
	 * @param sortieConsole
	 * @param erreur
	 */
	public abstract void afficher(ISortieConsole sortieConsole, T erreur);


	//-------------------------------------------------------------------------


	/**
	 * Affiche le message, la pile et les causes du {@link Throwable} sur
	 * l’{@link ISortieConsole}.
	 * 
	 * @param sortieConsole
	 * @param throwable
	 */
	public static void afficherThrowable(
			ISortieConsole sortieConsole,
			Throwable throwable) {

		sortieConsole.ecrireLigne(throwable.getClass().getName() + " : "
				+ throwable.toString());

		for (StackTraceElement i : throwable.getStackTrace())
			sortieConsole.ecrireLigne("    à " + i);

		Throwable cause = throwable.getCause();
		if (cause != null)
			afficherCommeCause(sortieConsole, cause);

	}

	private static void afficherCommeCause(
			ISortieConsole sortieConsole,
			Throwable throwable) {

		sortieConsole.ecrireLigne("Causé par :");
		sortieConsole.ecrire(throwable.getClass().getName() + " : ");
		sortieConsole.ecrireLigne(throwable.toString());

		for (StackTraceElement i : throwable.getStackTrace())
			sortieConsole.ecrireLigne("    à " + i);

		Throwable cause = throwable.getCause();
		if (cause != null)
			afficherCommeCause(sortieConsole, cause);

	}

	/**
	 * Un écouteur pour pouvoir facilement récupérer l’erreur.
	 * 
	 */
	public static interface Ecouteur {

		/**
		 * Lorsqu’une erreur s’est produite
		 * @param erreur
		 */
		public void onErreur(Object erreur);
	}
}
